home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 March
/
EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso
/
earcd
/
editor
/
chktex.lha
/
chktex
/
source
/
ChkTeX.c
next >
Wrap
C/C++ Source or Header
|
1996-01-25
|
21KB
|
894 lines
/*
* ChkTeX v1.2, finds typographic errors in (La)TeX files.
* Copyright (C) 1995-96 Jens T. Berger Thielemann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Contact the author at:
* Jens Berger
* Spektrumvn. 4
* N-0666 Oslo
* Norway
* E-mail: <jensthi@ifi.uio.no>
*
*
*/
#include "ChkTeX.h"
struct WordList
Silent = {0L},
Abbrev = {0L},
Linker = {0L},
IJAccent = {0L},
Italic = {0L},
UserWarn = {0L},
CmdLine = {0L},
PostLink = {0L};
struct Stack
CharStack = {0L};
struct WordList *CurRead = NULL; /* Where we'll put the words we find */
/************************************************************************/
const UBYTE BrOrder [NUMBRACKETS + 1] = "()[]{}";
/******************** VARS CHANGED RUNTIME ******************************/
ULONG Brackets [NUMBRACKETS];
BOOL AtLetter, /* Whether `@' is a letter or not. */
MathMode; /* Whether we're in math mode or not */
ULONG ErrPrint, /* # errors printed */
WarnPrint, /* # warnings printed */
UserSupp; /* # user suppressed warnings */
enum ItState /* Are we doing italics? */
ItState;
/***************************** ERROR MESSAGES ***************************/
#define INTFAULTMSG "INTERNAL FAULT OCCURED! PLEASE SUBMIT A BUG REPORT!\n"
#ifndef __LOCALIZED
char InternFault [] = INTFAULTMSG;
# define INTERNFAULT &InternFault[0]
#else
# define INTERNFAULT INTFAULTMSG
#endif
struct ErrMsg PrgMsgs [pmMaxFault + 1] =
{
{pmMinFault, etErr, TRUE,
INTERNFAULT},
{pmUnknownTerm, etErr, TRUE,
"Unknown terminal type - using normal verbosity."},
{pmNoFileMatch, etWarn, TRUE,
"No files matched the pattern `%s'."},
{pmNoTeXOpen, etErr, TRUE,
"Unable to open TeX files."},
{pmRename, etMsg, TRUE,
"Renaming `%s' as `%s'."},
{pmRenameErr, etErr, TRUE,
"Could not rename `%s' to `%s'."},
{pmOutOpen, etErr, TRUE,
"Unable to open output file."},
{pmOutTwice, etErr, TRUE,
"You can specify output file only once."},
{pmStrDupErr, etErr, TRUE,
"Unable to duplicate strings - no memory?"},
{pmWordListErr, etErr, TRUE,
"Unable to create wordlist - no memory?"},
{pmNoStackMem, etErr, TRUE,
"Unable to create stack - no memory?\n"},
{pmWarnNumErr, etErr, TRUE,
"Illegal warning number used."},
{pmVerbLevErr, etErr, TRUE,
"Illegal verbosity level."},
{pmNotPSDigit, etWarn, TRUE,
"`%c' is not a %s digit - ignored!"},
{pmEscCode, etWarn, TRUE,
"Unknown escape code `%c%c' - ignored!"},
{pmKeyWord, etErr, TRUE,
"Unsupported control word (`%s') encountered in file `%s'."},
{pmBraceCnt, etErr, TRUE,
/*{*/ "ERROR: `%s' has a faulty format - too many }'s! Unable to read.\n"},
{pmRsrcOpen, etWarn, TRUE,
"Could not open `%s', may cause unwanted behaviour."},
{pmMaxFault, etErr, TRUE,
INTERNFAULT},
};
struct ErrMsg LaTeXMsgs [emMaxFault + 1] =
{
{emMinFault, etErr, TRUE,
INTERNFAULT},
{emSpaceTerm, etWarn, TRUE,
"Command terminated with space."},
{emNBSpace, etWarn, TRUE,
"Non-breaking space (`~') should have been used."},
{emEnclosePar, etWarn, TRUE,
"You should enclose the previous parenthesis with `{}\'."},
{emItInNoIt, etWarn, TRUE,
"Italic correction (`\\/') found in non-italic buffer."},
{emItDup, etWarn, TRUE,
"Italic correction (`\\/') found more than once."},
{emNoItFound, etWarn, TRUE,
"No italic correction (`\\/') found."},
{emAccent, etWarn, TRUE,
"Accent command `%s' needs use of `\\%c%s'."},
{emWrongDash, etWarn, TRUE,
"Wrong length of dash may have been used."},
{emExpectC, etWarn, TRUE,
"`%c' expected, found `%c'."},
{emSoloC, etWarn, TRUE,
"Solo `%c' found."},
{emEllipsis, etWarn, TRUE,
"You should use `\\dots' to achieve an ellipsis."},
{emInterWord, etWarn, TRUE,
"Interword spacing (`\\ ') should been used."},
{emInterSent, etWarn, TRUE,
"Intersentence spacing (`\\@') should perhaps been used."},
{emNoArgFound, etErr, TRUE,
"Could not find argument for command `%s'."},
{emNoMatchC, etWarn, TRUE,
"No match found for character `%c'."},
{emMathStillOn, etWarn, TRUE,
"Mathmode still on at end of LaTeX file."},
{emNoMatchCC, etWarn, TRUE,
"Number of `%c' doesn't match the number of `%c'!"},
{emUseQuoteLiga, etWarn, TRUE,
"Use either `` or '' as an alternative to `\"'."},
{emUseOtherQuote, etWarn, TRUE,
"Use \"'\" (ASCII 39) instead of \"´\" (ASCII 180)."},
{emUserWarn, etWarn, TRUE,
"User-specified pattern found."},
{emNotIntended, etWarn, TRUE,
"This command might not be intended."},
{emComment, etMsg, FALSE,
"Comment displayed."},
{emThreeQuotes, etWarn, TRUE,
"Either %c\\,%c%c or %c%c\\,%c will look better."},
{emFalsePage, etWarn, TRUE,
"Delete this space to maintain correct pagereferences."},
{emEmbrace, etWarn, TRUE,
"You might wish to put this between a pair of `{}'"},
{emMaxFault, etErr, TRUE,
INTERNFAULT}
};
/************************************************************************/
/*
* Have to do things this way, to ease some checking throughout the
* program.
*/
NEWBUF(TmpBuffer, BUFLEN);
NEWBUF(CmdBuffer, BUFLEN);
NEWBUF(ReadBuffer, BUFLEN);
const STRPTR
Banner =
"ChkTeX v1.2 - Copyright 1995-96 Jens T. Berger Thielemann\n",
BigBanner =
"\n"
"ChkTeX comes with ABSOLUTELY NO WARRANTY; details on this and\n"
"distribution conditions in the GNU General Public License file.\n"
"Type \"ChkTeX -h\" for help, \"ChkTeX -i\" for distribution info.\n"
"To contact the author, write to:\n"
"Jens Berger, Spektrumvn. 4, N-0666 Oslo, Norway.\n"
"E-mail: <jensthi@ifi.uio.no>\n"
#ifdef AMIGA
"Press Ctrl-\\ to abort stdin input.\n"
#endif
,
Distrib =
"\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
"\n",
OnText =
"On",
OffText =
"Off",
HelpText =
"\n"
"\n"
" Usage of ChkTeX v1.2\n"
" ~~~~~~~~~~~~~~~~~~~~\n"
"\n"
" Template\n"
" ~~~~~~~~\n"
"chktex [-hiqr] [-v[0|1|2]] [-l <rcfile>] [-[w|e|m|n] <[0-24]>]\n"
" [-d <number>] [-o <outputfile>] [-b[0|1]] [-t[0|1]]\n"
" [-x[0|1]] [-g[0|1]]\n"
"\n"
"----------------------------------------------------------------------\n"
" Description of options:\n"
" ~~~~~~~~~~~~~~~~~~~~~~~\n"
"Misc. options\n"
"~~~~~~~~~~~~~\n"
" -h --help : This text.\n"
" -i --license : Show distribution information\n"
" -l --localrc : Read local .chktexrc formatted file.\n"
" -d --debug : Debug information. Give it a number.\n"
" -r --reset : Reset settings to default.\n"
"\n"
"Muting warning messages:\n"
"~~~~~~~~~~~~~~~~~~~~~~~~\n"
" -w --warnon : Makes msg # given a warning and turns it on.\n"
" -e --erroron : Makes msg # given an error and turns it on.\n"
" -m --msgon : Makes msg # given a message and turns it on.\n"
" -n --nowarn : Mutes msg # given.\n"
"\n"
"Output control flags:\n"
"~~~~~~~~~~~~~~~~~~~~~\n"
" -v --verbosity : How errors are displayed.\n"
" Default 1, 0=Less, 2=More.\n"
" -s --splitchar : String used to split fields when doing -v0\n"
" -o --output : Redirect error report to a file.\n"
" -q --quiet : Shuts up about version information.\n"
"\n"
"Boolean switches (1 -> enables / 0 -> disables):\n"
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
" -b --backup : Backup output file.\n"
" -t --tictoc : Display a twirling baton.\n"
" -v0 does an -t0, too.\n"
" -x --wipeverb : Ignore contents of `\\verb' commands.\n"
" -g --globalrc : Read global .chktexrc file.\n"
"\n"
"----------------------------------------------------------------------\n"
"If no LaTeX files are specified on the command line, we will read from\n"
"stdin. For explanation of warning/error messages, please consult the\n"
"main document ChkTeX.dvi or ChkTeX.ps.\n";
UBYTE EmptyString [] = "";
/*
* Options we will set.
*
*/
LONG Verbosity = vbNormal;
BOOL GlobalRC = TRUE, WipeVerb = TRUE, TicToc = TRUE, BackupOut = TRUE,
Quiet = FALSE, LicenseOnly = FALSE, UsingStdIn = FALSE;
LONG DebugLevel = 0;
FILE *OutputFile = NULL, *InputFile = NULL;
STRPTR OutputName = EmptyString, InputName = EmptyString;
UBYTE ConfigFile [BUFLEN] = RCFILE;
STRPTR Delimit = ":";
STRPTR PrgName;
/*
* End of config params.
*/
int main(int argc, char **argv)
{
int retval = EXIT_FAILURE, CurArg = 0L;
ULONG LineCount, Count;
#ifdef __LOCALIZED
InitStrings();
#endif
OutputFile = stdout;
PrgName = argv[0];
SetupVars();
ReadRC(ConfigFile);
if(CmdLine.Stack.Used)
{
ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
CmdLine.Stack.Used = 0L;
}
if(CurArg = ParseArgs((ULONG) argc, argv))
{
if(CmdLine.Stack.Used)
{
ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
CmdLine.Stack.Used = 0L;
}
if(!Quiet || LicenseOnly)
fprintf(stderr, Banner);
if(((CurArg == argc) && !Quiet) || LicenseOnly)
fprintf(stderr, BigBanner);
if(LicenseOnly)
fprintf(stderr, Distrib);
else
{
if(CurArg == argc)
UsingStdIn = TRUE;
for(Count = 0L;
Count < Abbrev.Stack.Used;
Count++)
strupr(Abbrev.Stack.Data[Count]);
if(DebugLevel)
ShowIntStatus();
if(OpenOut())
{
for(;;)
{
for(Count = 0; Count < NUMBRACKETS; Count++)
Brackets[Count] = 0L;
LineCount = 1;
ItState = itOff;
AtLetter = MathMode = FALSE;
ErrPrint = WarnPrint = UserSupp = 0L;
if(UsingStdIn) {
if(InputFile)
break;
else {
InputFile = stdin;
InputName = "stdin";
}
} else {
if((CurArg <= argc) || InputName) {
ifn(InputName = MatchFileName(NULL)) {
if(CurArg < argc)
InputName = MatchFileName(argv[CurArg++]);
}
if(InputName)
InputFile = fopen(InputName, "r");
else
break;
if(!InputFile) {
strcpy(TmpBuffer, InputName);
strcat(TmpBuffer, ".tex");
ifn(InputFile = fopen(TmpBuffer, "r")) {
PrintPrgErr(pmNoTeXOpen);
break;
}
}
}
}
if(InputFile && OutputFile)
{
if(Verbosity > vbSilent)
fprintf(OutputFile, "Scanning %s...\n",
InputName);
while(!ferror(OutputFile) && !ferror(InputFile) &&
fgets(ReadBuffer, BUFLEN-1, InputFile))
{
/* Make all spaces ordinary spaces */
strrep(ReadBuffer, '\n', ' ');
strrep(ReadBuffer, '\r', ' ');
strrep(ReadBuffer, '\t', ' ');
strcat(ReadBuffer, " ");
FindErr(ReadBuffer, LineCount);
if((Verbosity != vbSilent) && TicToc)
{
if(LineCount & 1)
tictoc();
}
LineCount++;
}
PrintStatus(LineCount);
retval = EXIT_SUCCESS;
}
}
}
}
}
exit(retval);
}
/*
* Opens the output file handle & possibly renames
*/
BOOL OpenOut(void)
{
BOOL Success = TRUE;
if(*OutputName)
{
if(BackupOut && fexists(OutputName))
{
strncpy(TmpBuffer, OutputName, BUFLEN - 4);
strcat(TmpBuffer, ".bak");
if(fexists(TmpBuffer))
remove(TmpBuffer);
if(!rename(OutputName, TmpBuffer))
PrintPrgErr(pmRename,
OutputName, TmpBuffer);
else
{
PrintPrgErr(pmRenameErr,
OutputName, TmpBuffer);
Success = FALSE;
}
}
if(Success)
{
ifn(OutputFile = fopen(OutputName, "w"))
{
PrintPrgErr(pmOutOpen);
Success = FALSE;
}
}
}
else
OutputFile = stdout;
return(Success);
}
#define BOOLDISP(var) ((var)? OnText : OffText)
#define SHOWSTAT(text, arg) fprintf(stderr, "\t" text ": %s\n", arg)
#define BOOLSTAT(name, var) SHOWSTAT(name, BOOLDISP(var))
#define SHOWWORD(text, list) fprintf(stderr, "\n" text "\n"); \
for(Cnt = 0L; \
Cnt < list.Stack.Used; \
Cnt++) \
fprintf(stderr, "\t%s\n", list.Stack.Data[Cnt])
/*
* Prints some of the internal flags; mainly for debugging purposes
*/
void ShowIntStatus(void)
{
ULONG Cnt;
STRPTR String;
fprintf(stderr, "There are %d warnings/error messages available:\n", emMaxFault - emMinFault - 1);
for(Cnt = emMinFault + 1; Cnt < emMaxFault; Cnt++)
{
switch(LaTeXMsgs[Cnt].Type)
{
case etWarn: String = "Warning"; break;
case etErr: String = "Error"; break;
case etMsg: String = "Message"; break;
}
fprintf(stderr, "Number: %2d, Type: %s, Status: %s\n"
"\tText: %s\n\n",
Cnt, String,
BOOLDISP(LaTeXMsgs[Cnt].InUse),
LaTeXMsgs[Cnt].Message);
}
fprintf(stderr, "Current flags include:\n");
switch(Verbosity)
{
case vbSilent: String = "Silent"; break;
case vbNormal: String = "Normal"; break;
case vbFancy: String = "Fancy"; break;
}
SHOWSTAT("Verbosity", String);
BOOLSTAT("Read global resource", GlobalRC);
BOOLSTAT("Wipe verbose commands", WipeVerb);
BOOLSTAT("Show twirling baton", TicToc);
BOOLSTAT("Backup outfile", BackupOut);
BOOLSTAT("Quiet mode", Quiet);
BOOLSTAT("Show license", LicenseOnly);
BOOLSTAT("Use stdin", UsingStdIn);
SHOWWORD("`Silent' commands:", Silent);
SHOWWORD("Abbreviations searched for:", Abbrev);
SHOWWORD("`Linked' commands:", Linker);
SHOWWORD("Accenting commands needing `\\i' and `\\j':", IJAccent);
SHOWWORD("Italizing commands:", Italic);
SHOWWORD("User patterns:", UserWarn);
}
/*
* Resets all flags (not wordlists) to their default values. Sets
* Outputfile to stdout.
*
*/
void ResetSettings(void)
{
GlobalRC = TRUE;
WipeVerb = TRUE;
TicToc = TRUE;
BackupOut = TRUE;
Quiet = FALSE;
LicenseOnly = FALSE;
UsingStdIn = FALSE;
DebugLevel = 0;
Verbosity = vbNormal;
if(OutputFile != stdout)
{
fclose(OutputFile);
OutputFile = stdout;
}
if(InputFile)
{
fclose(InputFile);
InputFile = NULL;
}
OutputName = EmptyString;
InputName = EmptyString;
}
/*
* Reads a numerical argument from the argument. Supports concatenation
* of arguments (main purpose)
*/
int ParseNumArg(LONG *Dest, /* Where to put the value */
LONG Default, /* Value to put in if no in argue */
STRPTR *Argument) /* optarg or similar */
{
if(Argument && *Argument && **Argument && isdigit(**Argument))
*Dest = strtol(*Argument, Argument, 10);
else
*Dest = Default;
return(ShiftArg(Argument));
}
/*
* Same as above; however, will toggle the boolean if user doesn't
* supply value
*/
int ParseBoolArg(BOOL *Dest, /* Boolean value */
STRPTR *Argument) /* optarg or similar */
{
LONG D = *Dest? 1L : 0L;
int Retval;
Retval = ParseNumArg(&D, *Dest? 0L : 1L, Argument);
*Dest = D ? TRUE : FALSE;
return(Retval);
}
/*
* Returns the first character in the string passed, updates the
* string pointer, if the string is non-empty.
*
* 0 if the string is empty.
*/
int ShiftArg(STRPTR *Argument) /* optarg or similar */
{
if(Argument && *Argument && **Argument)
return(*((STRPTR) (*Argument)++));
else
return 0;
}
/*
* Parses an argv similar array.
*/
int ParseArgs(ULONG argc, char **argv)
{
/* Needed for option parsing. */
static const
struct option long_options[] =
{
{"help", no_argument, 0L, 'h'},
{"localrc", required_argument, 0L, 'l'},
{"output", required_argument, 0L, 'o'},
{"warnon", required_argument, 0L, 'w'},
{"erroron", required_argument, 0L, 'e'},
{"msgon", required_argument, 0L, 'm'},
{"nowarn", required_argument, 0L, 'n'},
{"verbosity", optional_argument, 0L, 'v'},
{"debug", required_argument, 0L, 'd'},
{"reset", no_argument, 0L, 'r'},
{"quiet", no_argument, 0L, 'q'},
{"license", no_argument, 0L, 'i'},
{"splitchar", required_argument, 0L, 's'},
{"backup", optional_argument, 0L, 'b'},
{"globalrc", optional_argument, 0L, 'g'},
{"wipeverb", optional_argument, 0L, 'x'},
{"tictoc", optional_argument, 0L, 't'},
{0L, 0L, 0L, 0L}
};
int option_index = 0L;
int c, nextc, ErrType;
LONG Err;
int Retval = FALSE;
BOOL Success = FALSE;
int InUse;
enum
{
aeNoErr = 0,
aeArg, /* missing/bad required argument */
aeOpt, /* unknown option returned */
aeHelp, /* just a call for help */
aeMem /* no memory */
} ArgErr = aeNoErr;
optind = 0;
while(!ArgErr && ((c = getopt_long((int) argc, argv,
"hl:o:w:e:m:n:v::d:rqib::g::x::t::s:", long_options,
&option_index)) != EOF))
{
while(c) {
nextc = 0;
switch(c)
{
case 's':
ifn(Delimit = strdup(optarg)) {
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'd':
nextc = ParseNumArg(&DebugLevel, 0, &optarg);
break;
case 'i':
LicenseOnly = TRUE;
nextc = ShiftArg(&optarg);
break;
case 'q':
Quiet = TRUE;
nextc = ShiftArg(&optarg);
break;
/* Variation of Duff's device... */
do {
case 'w':
ErrType = etWarn;
InUse = TRUE;
break;
case 'e':
ErrType = etErr;
InUse = TRUE;
break;
case 'm':
ErrType = etMsg;
InUse = TRUE;
break;
case 'n':
InUse = FALSE;
} while(FALSE);
nextc = ParseNumArg(&Err, -1, &optarg);
if(betw(emMinFault, Err, emMaxFault)) {
LaTeXMsgs[Err].Type = ErrType;
LaTeXMsgs[Err].InUse = InUse;
} else {
ArgErr = aeOpt;
PrintPrgErr(pmWarnNumErr);
}
break;
case 'g':
nextc = ParseBoolArg(&GlobalRC, &optarg);
if(!GlobalRC) {
Silent.Stack.Used = 0L;
Abbrev.Stack.Used = 0L;
Linker.Stack.Used = 0L;
IJAccent.Stack.Used = 0L;
Italic.Stack.Used = 0L;
UserWarn.Stack.Used = 0L;
}
break;
case 'r':
ResetSettings();
nextc = ShiftArg(&optarg);
break;
case 'l':
if(optarg)
ReadRC(optarg);
break;
case 'v':
nextc = ParseNumArg(&Verbosity, vbFancy, &optarg);
switch(Verbosity)
{
case vbSilent:
case vbNormal:
break;
case vbFancy:
if(TermType != tmUnknown) {
Verbosity = vbFancy;
} else {
PrintPrgErr(pmUnknownTerm);
Verbosity = vbNormal;
}
break;
default:
PrintPrgErr(pmVerbLevErr);
ArgErr = aeArg;
break;
}
break;
case 'o':
if(optarg)
{
if(*OutputName)
{
PrintPrgErr(pmOutTwice);
ArgErr = aeOpt;
}
else
{
ifn(OutputName = strdup(optarg))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
}
}
break;
case 't':
nextc = ParseBoolArg(&TicToc, &optarg);
break;
case 'x':
nextc = ParseBoolArg(&WipeVerb, &optarg);
break;
case 'b':
nextc = ParseBoolArg(&BackupOut, &optarg);
break;
case 'h':
case '?':
default:
ArgErr = aeHelp;
break;
}
c = nextc;
}
}
if((argc >= optind) && !strcmp(argv[optind], "?"))
ArgErr = aeHelp;
if(ArgErr)
{
fputs(Banner, stderr);
fputs(BigBanner, stderr);
fputs(HelpText, stderr);
Success = FALSE;
}
else
Success = TRUE;
if(Success)
Retval = optind;
return(Retval);
}
/*
* Outputs a program error.
*/
void PrintPrgErr(enum PrgErrNum Error, ...)
{
STRPTR Type;
va_list MsgArgs;
if(betw(pmMinFault, Error, pmMaxFault)) {
switch(PrgMsgs[Error].Type) {
case etWarn:
Type = "WARNING";
break;
case etErr:
Type = "ERROR";
break;
case etMsg:
Type = "NOTE";
break;
}
fprintf(stderr, "%s: %s -- ", PrgName, Type);
va_start(MsgArgs, Error);
vfprintf(stderr, PrgMsgs[Error].Message, MsgArgs);
va_end(MsgArgs);
fputc('\n', stderr);
}
}